/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://github.com/payara/Payara/blob/main/LICENSE.txt
 * See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at legal/OPEN-SOURCE-LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2022] [Payara Foundation and/or its affiliates]
package org.glassfish.webservices;

import com.sun.enterprise.deployment.*;
import com.sun.xml.ws.api.server.*;
import com.sun.xml.ws.transport.http.servlet.ServletAdapter;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.deployment.versioning.VersioningUtils;
import org.jvnet.hk2.annotations.Service;

import com.sun.enterprise.container.common.spi.WebServiceReferenceManager;
import com.sun.xml.ws.api.FeatureConstructor;
import com.sun.xml.ws.resources.ModelerMessages;

import jakarta.inject.Inject;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.InitialContext;
import javax.xml.namespace.QName;
import jakarta.xml.ws.soap.MTOMFeature;
import jakarta.xml.ws.soap.AddressingFeature;
import jakarta.xml.ws.WebServiceFeature;
import jakarta.xml.ws.RespectBindingFeature;
import jakarta.xml.ws.WebServiceException;
import jakarta.xml.ws.spi.WebServiceFeatureAnnotation;
import java.io.*;
import java.lang.reflect.*;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.PrivilegedActionException;
import java.net.URL;

/**
 * This class acts as a service to resolve the
 * </code>jakarta.xml.ws.WebServiceRef</code> references and also
 * <code>jakarta.xml.ws.WebServiceContext</code> Whenever a lookup is done from
 * GlassfishNamingManagerImpl these methods are invoked to resolve the
 * references
 *
 * @author Bhakti Mehta
 */
@Service
public class WebServiceReferenceManagerImpl implements WebServiceReferenceManager {

    @Inject
    private ServerEnvironment serverEnv;

    protected Logger logger = LogUtils.getLogger();

    public Object getWSContextObject() {
        return new WebServiceContextImpl();
    }

    public Object resolveWSReference(ServiceReferenceDescriptor desc, Context context)
            throws NamingException {

        //Taken from NamingManagerImpl.getClientServiceObject
        Class serviceInterfaceClass = null;
        Object returnObj = null;
        WsUtil wsUtil = new WsUtil();

        //Implementation for new lookup element in WebserviceRef
        InitialContext iContext = new InitialContext();
        if (desc.hasLookupName()) {
            return iContext.lookup(desc.getLookupName());

        }

        try {

            WSContainerResolver.set(desc);

            ClassLoader cl = Thread.currentThread().getContextClassLoader();

            serviceInterfaceClass = cl.loadClass(desc.getServiceInterface());

            resolvePortComponentLinks(desc);

            jakarta.xml.ws.Service jaxwsDelegate = null;
            Object injValue = null;

            // The target is probably a post JAXRPC-1.1- based service;
            // If Service Interface class is set, check if it is indeed a subclass of Service
            // initiateInstance should not be called if the user has given jakarta.xml.ws.Service itself
            // as the interface through DD
            if (jakarta.xml.ws.Service.class.isAssignableFrom(serviceInterfaceClass)
                    && !jakarta.xml.ws.Service.class.equals(serviceInterfaceClass)) {
                // OK - the interface class is indeed the generated service class; get an instance
                injValue = initiateInstance(serviceInterfaceClass, desc);
            } else {
                // First try failed; Try to get the Service class type from injected field name
                // and from there try to get an instance of the service class

                // I assume the at all inejction target are expecting the SAME service
                // interface, therefore I take the first one.
                if (desc.isInjectable()) {

                    InjectionTarget target = desc.getInjectionTargets().iterator().next();
                    Class serviceType = null;
                    if (target.isFieldInjectable()) {
                        java.lang.reflect.Field f = target.getField();
                        if (f == null) {
                            String fName = target.getFieldName();
                            Class targetClass = cl.loadClass(target.getClassName());
                            try {
                                f = targetClass.getDeclaredField(fName);
                            } catch (java.lang.NoSuchFieldException nsfe) {
                            }// ignoring exception
                        }
                        if (f != null) {
                            serviceType = f.getType();
                        }
                    }
                    if (target.isMethodInjectable()) {
                        Method m = target.getMethod();
                        if (m == null) {
                            String mName = target.getMethodName();
                            Class targetClass = cl.loadClass(target.getClassName());
                            try {
                                m = targetClass.getDeclaredMethod(mName);
                            } catch (java.lang.NoSuchMethodException nsfe) {
                            }// ignoring exception
                        }
                        if (m != null && m.getParameterTypes().length == 1) {
                            serviceType = m.getParameterTypes()[0];
                        }
                    }
                    if (serviceType != null) {
                        Class loadedSvcClass = cl.loadClass(serviceType.getCanonicalName());
                        injValue = initiateInstance(loadedSvcClass, desc);
                    }
                }
            }
            // Unable to get hold of generated service class -> try the Service.create avenue to get a Service
            if (injValue == null) {
                // Here create the service with WSDL (overridden wsdl if wsdl-override is present)
                // so that JAXWS runtime uses this wsdl @ runtime
                jakarta.xml.ws.Service svc
                        = jakarta.xml.ws.Service.create((new WsUtil()).privilegedGetServiceRefWsdl(desc),
                                desc.getServiceName());
                jaxwsDelegate = new JAXWSServiceDelegate(desc, svc, cl);
            }

            // check if this is a post 1.1 web service
            if (jakarta.xml.ws.Service.class.isAssignableFrom(serviceInterfaceClass)) {
                // This is a JAXWS based webservice client;
                // process handlers and mtom setting
                // moved test for handlers into wsUtil, in case
                // we have to add system handler

                jakarta.xml.ws.Service service
                        = (injValue != null
                                ? (jakarta.xml.ws.Service) injValue : jaxwsDelegate);

                if (service != null) {
                    // Now configure client side handlers
                    wsUtil.configureJAXWSClientHandlers(service, desc);
                }
                // the requested resource is not the service but one of its port.
                if (injValue != null && desc.getInjectionTargetType() != null) {
                    Class requestedPortType = service.getClass().getClassLoader().loadClass(desc.getInjectionTargetType());
                    ArrayList<WebServiceFeature> wsFeatures = getWebServiceFeatures(desc);
                    if (wsFeatures.size() > 0) {
                        injValue = service.getPort(requestedPortType, wsFeatures.toArray(new WebServiceFeature[wsFeatures.size()]));
                    } else {
                        injValue = service.getPort(requestedPortType);
                    }
                }

            }

            if (jaxwsDelegate != null) {
                returnObj = jaxwsDelegate;
            } else if (injValue != null) {
                returnObj = injValue;
            }
        } catch (PrivilegedActionException pae) {
            logger.log(Level.WARNING, LogUtils.EXCEPTION_THROWN, pae);
            NamingException ne = new NamingException();
            ne.initCause(pae.getCause());
            throw ne;
        } catch (Exception e) {
            logger.log(Level.WARNING, LogUtils.EXCEPTION_THROWN, e);
            NamingException ne = new NamingException();
            ne.initCause(e);
            throw ne;
        } finally {
            WSContainerResolver.unset();
        }

        return returnObj;
    }

    private Object initiateInstance(Class svcClass, ServiceReferenceDescriptor desc)
            throws Exception {

        //TODO BM if JBI needs this reenable it
        /*com.sun.enterprise.webservice.ServiceRefDescUtil descUtil =
           new com.sun.enterprise.webservice.ServiceRefDescUtil();
        descUtil.preServiceCreate(desc);*/
        WsUtil wsu = new WsUtil();
        URL wsdlFile = wsu.privilegedGetServiceRefWsdl(desc);
        /* TODO BM resolve catalog
        // Check if there is a catalog for this web service client
        // If so resolve the catalog entry
        String genXmlDir;
        if(desc.getBundleDescriptor().getApplication() != null) {
            genXmlDir = desc.getBundleDescriptor().getApplication().getGeneratedXMLDirectory();
            if(!desc.getBundleDescriptor().getApplication().isVirtual()) {
                String subDirName = desc.getBundleDescriptor().getModuleDescriptor().getArchiveUri();
                genXmlDir += (File.separator+subDirName.replaceAll("\\.",  "_"));
            }
        } else {
            // this is the case of an appclient being run as class file from command line
            genXmlDir = desc.getBundleDescriptor().getModuleDescriptor().getArchiveUri();
        }
        File catalogFile = new File(genXmlDir,
                desc.getBundleDescriptor().getDeploymentDescriptorDir() +
                    File.separator + "jax-ws-catalog.xml");

        if(catalogFile.exists()) {
            wsdlFile = wsu.resolveCatalog(catalogFile, desc.getWsdlFileUri(), null);
        }   */

        Object obj = null;

        java.lang.reflect.Constructor cons = svcClass.getConstructor(new Class[]{java.net.URL.class,
            javax.xml.namespace.QName.class});
        try {
            obj
                    = cons.newInstance(wsdlFile, desc.getServiceName());
        } catch (Exception e) {
            /*
             * If WSDL URL is not accessible over http, trying to get an instance via
             * reflection results in InvocationTargetException. If InvocationTargetException
             * is thrown,then catch the exception and generate wsdl in generated xml directory
             * of the application being deployed.
             */
            if (e instanceof InvocationTargetException) {
                URL optionalWsdlURL = generateWsdlFile(desc);
                if (optionalWsdlURL == null) {
                    throw e;
                }
                obj = cons.newInstance(optionalWsdlURL, desc.getServiceName());
            }
        }

        /*TODO BM if jbi needs this reenable it
        descUtil.postServiceCreate();
         */
        return obj;

    }

    /**
     * This method returns the location where optional wsdl file will be
     * generated. The directory will be a directory having same name as
     * WebService name inside application's generated xml directory. The name of
     * the wsdl file will be wsdl.xml e.g. if application name is test and
     * service name is Translator, then the location of wsdl will be
     * $Glassfish_home/domains/domain1/generated/xml/test/Translator/wsdl.xml
     *
     * @param desc ServiceReferenceDescriptor
     * @return optional wsdl file location
     */
    private File getOptionalWsdlLocation(ServiceReferenceDescriptor desc) {
        File generatedXmlDir = serverEnv.getApplicationGeneratedXMLPath();
        return new File(new File(new File(generatedXmlDir,
                VersioningUtils.getRepositoryName(desc.getBundleDescriptor().getApplication()
                        .getRegistrationName())), desc.getServiceLocalPart()), "wsdl.xml");
    }

    private void createParentDirs(File optionalWsdlLocation) throws IOException {
        File parent = optionalWsdlLocation.getParentFile();
        mkDirs(parent);
    }

    private URL generateWsdlFile(ServiceReferenceDescriptor desc) throws IOException {

        /*
        * Following piece of code is basically a copy-paste from JAXWSServlet's
        * doGet method (line 230) and from com.sun.xml.ws.transport.http.servlet.HttpAdapter's
        * publishWSDL method (line 587).This piece of code is not completely clear to me,
        * what I have understood so far is, during WSEndPoint creation on line 267 in
        * WSServletContextListener, com.sun.xml.ws.server.EndPointFactory.create (line 116)
        * method is invoked where ServiceDocumentImpl instance is created, which is later
        * being fetched here to generate wsdl. When serviceDefinition.getPrimary() is
        * invoked, basically it returns the reference to wsdl document marked as primary
        * wsdl inside ServiceDefinition. Probably we can directly fetch this wsdl
        * but for now I will go with the way it has been implemented in HttpAdapter.
         */
        File optionalWsdl = getOptionalWsdlLocation(desc);

        /*
         * Its possible that in a given application there are more than one Filter/Servlet
         * with loadOnStartup=1 having WebServiceRef annotation,or WebServiceRef
         * annotation is used at multiple places within the same Filter/Servlet,
         * in which case, when processing is going on for second filter/servlet
         * or annotation referring to the same web service, then wsdl file has
         * already been generated at this point in time and there is no need to
         * generate it again.
         */
        if (optionalWsdl.exists()) {
            return optionalWsdl.toURI().toURL();
        }

        createParentDirs(optionalWsdl);
        ServletAdapter targetEndpoint = getServletAdapter(desc);
        if (targetEndpoint == null) {
            return null;
        }
        ServiceDefinition serviceDefinition = targetEndpoint.getServiceDefinition();
        Iterator wsdlnum = serviceDefinition.iterator();
        SDDocument wsdlDocument = null;
        while (wsdlnum.hasNext()) {
            SDDocument xsdnum = (SDDocument) wsdlnum.next();
            if (xsdnum == serviceDefinition.getPrimary()) {
                wsdlDocument = xsdnum;
                break;
            }
        }

        if (wsdlDocument == null) {
            return null;
        }

        OutputStream outputStream = null;
        try {
            outputStream = new BufferedOutputStream(new FileOutputStream(optionalWsdl));
            PortAddressResolver portAddressResolver = targetEndpoint
                    .getPortAddressResolver(getBaseAddress(desc.getWsdlFileUrl()));
            DocumentAddressResolver resolver = targetEndpoint.getDocumentAddressResolver(portAddressResolver);
            wsdlDocument.writeTo(portAddressResolver, resolver, outputStream);
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }

        return optionalWsdl.toURI().toURL();
    }

    /**
     * Returns ServletAdapter instance holding wsdl for the WebService being
     * referred in WebServiceRef annotation.
     *
     * @param desc ServiceReferenceDescriptor
     * @return ServletAdapter instance having wsdl contents.
     */
    private ServletAdapter getServletAdapter(ServiceReferenceDescriptor desc) {

        WebBundleDescriptor webBundle = null;
        WebServicesDescriptor webServicesDescriptor = null;

        /*
         * If flow has reached to this part of the code,then in all likelihood,
         * the wsdl is available under the context root of the a web application
         * and hence the BundleDescriptor being referred in ServiceReferenceDescriptor
         * is an instance of WebBundleDescriptor.
         */
        if (desc.getBundleDescriptor() instanceof WebBundleDescriptor) {

            webBundle = ((WebBundleDescriptor) desc.getBundleDescriptor());

        } else {

            /*
             * If above assumption is not true, then make one last attempt to fetch
             * all required params from the wsdl url stored in ServiceReferenceDescriptor.
             */
            return getServletAdapterBasedOnWsdlUrl(desc);
        }

        /*
         * Get WebServicesDescriptor from WebBundleDescriptorImpl, Since we are
         * dealing with WebServiceRef annotation here, WebServicesDescriptor ought to have
         * reference to WebService in question. WebServicesDescriptor is never null as it
         * is being initialized at class level in BundleDescriptor.
         */
        WebServicesDescriptor wsDesc = webBundle.getWebServices();

        assert wsDesc != null;

        /*
         * WebService name is being fetched by invoking getServiceLocalPart()
         * on ServiceReferenceDescriptor. ServiceLocalPart is set when WebServiceClient
         * annotated class is processed inside
         * org.glassfish.webservices.connector.annotation.handlers.WebServiceRefHandler's
         * processAWsRef call (line 339). WebServiceClient annotation have name param pointing
         * to webservice in question.
         */
        assert desc.getServiceLocalPart() != null;

        WebService webService = wsDesc.getWebServiceByName(desc.getServiceLocalPart());

        /*
         * If an unlikely event when there is no associated webService or desc.getServiceLocalPart()
         * itself is null, then fall back on fetching ServletAdapter based on wsdl url.
         */
        if (webService == null) {
            return getServletAdapterBasedOnWsdlUrl(desc);
        }

        String contextRoot = webBundle.getContextRoot();
        String webSevicePath = null;
        String publishingContext = null;
        /*
         * Iterate over all associated WebServiceEndPoints for this WebService
         * and break when condition specified in if block is met. This is the same
         * condition based on which wsdl url is set in first place for this
         * ServiceReferenceDescriptor and hence this must hold true in this context too.
         */

        for (WebServiceEndpoint endpoint : webService.getEndpoints()) {
            if (desc.getServiceName().equals(endpoint.getServiceName())
                    && desc.getServiceNamespaceUri().equals(endpoint.getWsdlService().getNamespaceURI())) {
                String endPointAddressURI = endpoint.getEndpointAddressUri();
                if (endPointAddressURI == null || endPointAddressURI.length() == 0) {
                    return null;
                }
                webSevicePath = endPointAddressURI.startsWith("/") ? endPointAddressURI : ("/" + endPointAddressURI);
                publishingContext = "/" + endpoint.getPublishingUri() + "/" + webService.getWsdlFileUri();
                Adapter adapter = JAXWSAdapterRegistry.getInstance()
                        .getAdapter(contextRoot, webSevicePath, publishingContext);
                return adapter instanceof ServletAdapter ? (ServletAdapter) adapter : null;

            }
        }
        return null;
    }

    /**
     * This method basically is a fall back mechanism to fetch required
     * parameters from wsdl url stored in ServiceReferenceDescriptor. The flow
     * reaches here only in case where required parameters could not be fetched
     * from WebBundleDescriptor.
     *
     * @param desc ServiceReferenceDescriptor
     * @return ServletAdapter instance having wsdl contents.
     */
    private ServletAdapter getServletAdapterBasedOnWsdlUrl(ServiceReferenceDescriptor desc) {

        if (logger.isLoggable(Level.INFO)) {
            logger.log(Level.INFO, LogUtils.SERVLET_ADAPTER_BASED_ON_WSDL_URL,
                    new Object[]{desc.getServiceLocalPart(), desc.getWsdlFileUrl()});
        }

        URL wsdl = desc.getWsdlFileUrl();
        if (wsdl == null || wsdl.getPath() == null) {
            return null;
        }
        String wsdlPath = wsdl.getPath().trim();
        if (!wsdlPath.contains(WebServiceEndpoint.PUBLISHING_SUBCONTEXT)) {
            return null;
        }

        /*
          * WsdlPath indeed contains the WebServiceEndpoint.PUBLISHING_SUBCONTEXT,
          * e.g.assuming that context root is test and Service name is Translator
          * then wsdl url must be in the following format :
          * /test/Translator/__container$publishing$subctx/null?wsdl
         */
        String contextRootAndPath = wsdlPath.substring(1,
                wsdlPath.indexOf(WebServiceEndpoint.PUBLISHING_SUBCONTEXT) - 1); // test/Translator
        if (!(contextRootAndPath.length() > 0)) {
            return null;
        }
        String[] contextRootAndPathArray = contextRootAndPath.split("/"); // {test, Translator}
        if (contextRootAndPathArray.length != 2) {
            return null;
        }
        if (contextRootAndPathArray[0] == null) {
            return null;
        }
        String contextRoot = "/" + contextRootAndPathArray[0];  // /test
        if (contextRootAndPathArray[1] == null) {
            return null;
        }
        String webSevicePath = "/" + contextRootAndPathArray[1]; // /Translator
        String urlPattern = wsdlPath.substring(contextRoot.length());
        Adapter adapter = JAXWSAdapterRegistry.getInstance()
                .getAdapter(contextRoot, webSevicePath, urlPattern);
        return adapter instanceof ServletAdapter ? (ServletAdapter) adapter : null;

    }

    private static String getBaseAddress(URL wsdlUrl) {
        return wsdlUrl.getProtocol() + "://" + wsdlUrl.getHost() + ":" + wsdlUrl.getPort();
    }

    private void mkDirs(File f) {
        if (!f.mkdirs() && logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, LogUtils.DIR_EXISTS, f);
        }
    }

    private ArrayList<WebServiceFeature> getWebServiceFeatures(ServiceReferenceDescriptor desc) {
        /**
         * JAXWS 2.2 enables @MTOM, @Addressing @RespectBinding on WebServiceRef
         * If these are present use the Service(url,wsdl,features) constructor
         */
        ArrayList<WebServiceFeature> wsFeatures = new ArrayList<WebServiceFeature>();
        if (desc.isMtomEnabled()) {
            wsFeatures.add(new MTOMFeature(true, desc.getMtomThreshold()));
        }
        com.sun.enterprise.deployment.Addressing add = desc.getAddressing();
        if (add != null) {
            wsFeatures.add(new AddressingFeature(
                    add.isEnabled(), add.isRequired(), getResponse(add.getResponses())));
        }
        com.sun.enterprise.deployment.RespectBinding rb = desc.getRespectBinding();
        if (rb != null) {
            wsFeatures.add(new RespectBindingFeature(rb.isEnabled()));
        }
        Map<Class<? extends Annotation>, Annotation> otherAnnotations
                = desc.getOtherAnnotations();
        Iterator it = otherAnnotations.values().iterator();
        while (it.hasNext()) {
            wsFeatures.add(getWebServiceFeatureBean((Annotation) it.next()));
        }

        return wsFeatures;
    }

    private AddressingFeature.Responses getResponse(String s) {
        if (s != null) {
            return AddressingFeature.Responses.valueOf(AddressingFeature.Responses.class, s);
        } else {
            return AddressingFeature.Responses.ALL;
        }

    }

    private void resolvePortComponentLinks(ServiceReferenceDescriptor desc)
            throws Exception {

        // Resolve port component links to target endpoint address.
        // We can't assume web service client is running in same VM
        // as endpoint in the intra-app case because of app clients.
        //
        // Also set port-qname based on linked port's qname if not
        // already set.
        for (Iterator iter = desc.getPortsInfo().iterator(); iter.hasNext();) {
            ServiceRefPortInfo portInfo = (ServiceRefPortInfo) iter.next();

            if (portInfo.isLinkedToPortComponent()) {
                WebServiceEndpoint linkedPortComponent
                        = portInfo.getPortComponentLink();

                // XXX-JD we could at this point try to figure out the
                // endpoint-address from the ejb wsdl file but it is a
                // little complicated so I will leave it for post Beta2
                if (!(portInfo.hasWsdlPort())) {
                    portInfo.setWsdlPort(linkedPortComponent.getWsdlPort());
                }
            }
        }
    }

    private WebServiceFeature getWebServiceFeatureBean(Annotation a) {
        WebServiceFeatureAnnotation wsfa = a.annotationType().getAnnotation(WebServiceFeatureAnnotation.class);

        Class<? extends WebServiceFeature> beanClass = wsfa.bean();
        WebServiceFeature bean;

        Constructor ftrCtr = null;
        String[] paramNames = null;
        for (Constructor con : beanClass.getConstructors()) {
            FeatureConstructor ftrCtrAnn = (FeatureConstructor) con.getAnnotation(FeatureConstructor.class);
            if (ftrCtrAnn != null) {
                if (ftrCtr == null) {
                    ftrCtr = con;
                    paramNames = ftrCtrAnn.value();
                } else {
                    throw new WebServiceException(ModelerMessages.RUNTIME_MODELER_WSFEATURE_MORETHANONE_FTRCONSTRUCTOR(a, beanClass));
                }
            }
        }
        if (ftrCtr == null) {
            throw new WebServiceException(ModelerMessages.RUNTIME_MODELER_WSFEATURE_NO_FTRCONSTRUCTOR(a, beanClass));
        }
        if (ftrCtr.getParameterTypes().length != paramNames.length) {
            throw new WebServiceException(ModelerMessages.RUNTIME_MODELER_WSFEATURE_ILLEGAL_FTRCONSTRUCTOR(a, beanClass));
        }

        try {
            Object[] params = new Object[paramNames.length];
            for (int i = 0; i < paramNames.length; i++) {
                Method m = a.annotationType().getDeclaredMethod(paramNames[i]);
                params[i] = m.invoke(a);
            }
            bean = (WebServiceFeature) ftrCtr.newInstance(params);
        } catch (Exception e) {
            throw new WebServiceException(e);
        }

        return bean;
    }

}
